package com.wsd.bookkeeping.service.impl;

import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsd.bookkeeping.entity.Account;
import com.wsd.bookkeeping.form.InviteForm;
import com.wsd.bookkeeping.form.TransferForm;
import com.wsd.bookkeeping.mapper.AccountMapper;
import com.wsd.bookkeeping.mapper.UserInviteMapper;
import com.wsd.bookkeeping.service.AccountService;
import com.wsd.bookkeeping.service.RecordService;
import com.wsd.bookkeeping.vo.AccountAdminVO;
import com.wsd.bookkeeping.vo.AccountUserVO;
import com.wsd.bookkeeping.vo.AccountVO;
import com.wsd.bookkeeping.vo.LedgerUserInfo;
import com.wsd.common.service.QrCodeService;
import com.wsd.common.utils.DateUtils;
import com.wsd.exception.RRException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Resource
    private UserInviteMapper userInviteMapper;

    @Resource
    private QrCodeService qrCodeService;

    @Resource
    private RecordService recordService;

    @Value("${qr-code.location}")
    private String path;
    @Value("${qr-code.invite-path}")
    private String invitePath;  // 用户扫描二维码后跳转到的页面
    @Value("${wx.index-account-quantity}")
    private Integer indexAccountQuantity;


    @Override
    public List<AccountVO> queryUserAccountList(Integer userId) throws InterruptedException {
        List<AccountVO> accountVOList = new ArrayList<>(indexAccountQuantity * 2);

        // 创建一个线程数量为2的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(() -> {
            // 用户创建的账本
            List<AccountVO> list = baseMapper.queryAccountListOfSelf(userId, indexAccountQuantity);
            if (list != null) {
                accountVOList.addAll(list);
            }
        });
        executorService.submit(() -> {
            // 用户加入的账本
            List<AccountVO> list = baseMapper.queryAccountListOfJoin(userId, indexAccountQuantity);
            if (list != null) {
                accountVOList.addAll(list);
            }
        });
        executorService.shutdown();
        executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
        return accountVOList;
    }

    @Override
    public List<Map<String, Object>> queryUserListByBookId(Integer bookId) {
        return baseMapper.queryUserListByBookId(bookId);
    }

    @Override
    public AccountAdminVO queryAccountAdminByBookId(Integer bookId) {
        return baseMapper.queryAccountAdminByBookId(bookId);
    }

    @Override
    public List<LedgerUserInfo> queryUserInfoListByBookId(Integer bookId) {
        return baseMapper.queryUserInfoListByBookId(bookId);
    }

    @Override
    public List<AccountUserVO> queryAccountUsersByBookId(Integer bookId) {
        return baseMapper.queryAccountBookUserListByBookId(bookId);
    }

    /**
     * 获取邀请二维码，并将其保存在磁盘中
     * @param inviteForm
     * @param mode 模式 true表示第一次创建 | false表示已经创建了，但是无法找到文件
     * @return 图片名称
     */
    public InputStream generateInviteImageAndWriteToDisk(InviteForm inviteForm, boolean mode) throws Exception {
        String imgName = UUID.randomUUID() + ".jpeg";
        String fullPath = path + imgName;  // 文件全路径

        String data = "uid=" + inviteForm.getUserId() + "&bid=" + inviteForm.getBookId() + "&s=" + (Objects.isNull(inviteForm.getScene()) ? "" : inviteForm.getScene());

        InputStream inputStream = qrCodeService.generateQtCodeAsStream(invitePath, data);  // 向微信服务器发送求，获取二维码buf

        OutputStream outputStream = new FileOutputStream(fullPath);

        IoUtil.copy(inputStream, outputStream);  // 保存文件
        IoUtil.close(outputStream);
        inputStream.reset();  // 因为拷贝流的时候修改了pos，复用时需要将其复原

        if (mode) {
            userInviteMapper.saveImagePath(inviteForm.getUserId(), inviteForm.getBookId(), imgName);  // 保存图片名称记录
        } else {
            userInviteMapper.updateInviteImagePathByUserId(inviteForm.getUserId(), inviteForm.getBookId(), imgName);  // 修改图片记录
        }
        return inputStream;
    }

    /**
     * 将二维码写入到输出流
     * @param outputStream 输出流
     */
    @Override
    public void writeInviteImageToOutputStream(InviteForm inviteForm, OutputStream outputStream) throws Exception {
        String imagePath = userInviteMapper.getInviteImagePathByUserId(inviteForm.getUserId(), inviteForm.getBookId());
        String fullPath = path + imagePath;
        InputStream inputStream;
        File file = new File(fullPath);

        if (Objects.isNull(imagePath)) {  // 当前账单还没有邀请二维码
            inputStream = this.generateInviteImageAndWriteToDisk(inviteForm, true);
        } else if (!file.exists()) {  // 图片不存在
            inputStream = this.generateInviteImageAndWriteToDisk(inviteForm, false);
        } else {  // 图片存在
            inputStream = new FileInputStream(file);
        }

        IoUtil.copy(inputStream, outputStream);  // 将图片拷贝到输出流
        IoUtil.close(outputStream);
        IoUtil.close(inputStream);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeAccountById(Integer accountId) {
        boolean r1 = false, r2 = false;
        r1 = this.removeById(accountId);  // 删除账本记录
        r2 = recordService.deleteByBookId(accountId) >= 0;  // 删除记账记录

        if (r1 && r2) return true;
        throw new RRException("删除失败");
    }

    @Override
    public boolean transferAuthority(TransferForm form) {
        // 修改
        if (baseMapper.transferAuthority(form) > 0) {
            form.setCurrentDate(DateUtils.currentDateTimeNString());
            // 记录日志
            baseMapper.saveTransferAuthorityLog(form);
            return true;
        }

        return false;
    }

    @Override
    public boolean isEfficientBook(Integer bookId) {
        return baseMapper.selectById(bookId) != null;
    }
}
